Skip to content

Fixes infinite recursion introduced by patch to SplFixedArray #8105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 24, 2022

Conversation

TysonAndre
Copy link
Contributor

Closes GH-8079

Under most circumstances, the zend_hash_index_update is setting the value to
itself when the hash table's reference count is already 2

@TysonAndre
Copy link
Contributor Author

I was trying to avoid changing the memory layout of the FixedArray if possible in a patch release for a non-security change (e.g. in case FFI or debuggers relied on exact memory layout), but that seems excessive in retrospect.
(The declaration is in ext/spl/spl_fixedarray.c, not a header file)

  • setting a flag in write_dimension, read_dimension based on type, unset_dimension, setSize, etc would be the alternative

Either reverting the previous patch or changing the handling of var_export so that class implementations aren't responsible for it seems possibly better, though there's less motivation for the latter approach with nothing other than SplFixedArray in php-src having custom implementations and cyclic data structures and SplFixedArray having limited use.

@TysonAndre TysonAndre requested review from dstogov and nikic February 17, 2022 00:47
if (!spl_fixedarray_object_needs_rebuild(intern, ht, j)) {
/* Return the same hash table so that recursion cycle detection works in internal functions. */
return ht;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting approach, but I afraid it's too expensive. I thought about setting some flag on SplFixedArray modification.

BTW this leads me to another idea - we may try to return the same ht if it's already protected from recursion.

    if (GC_IS_RECURSIVE(ht)) {
        return ht;
    }

Ideally, we should switch from get_properties to get_properties_for and do this for everything except ZEND_PROP_PURPOSE_ARRAY_CAST. Thanks for persistence. I hope, we will fix this soon.

Now, I also see a mesh in ext/standard/var.c that uses different protection mechanisms in different places. Did you propose a PR that tried to change this? I suppose, Z_IS_RECURSIVE_P/GC_IS_RECURSIVE were used on purpose, but I can't remember anything. @nikic do you have any thoughts?

Copy link
Contributor Author

@TysonAndre TysonAndre Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, I also see a mesh in ext/standard/var.c that uses different protection mechanisms in different places. Did you propose a PR that tried to change this?

Yes, I'd proposed #8046 Make var_export/debug_zval_dump first check for infinite recursion on the object to try to change this to always check for infinite recursion on the object before get_properties/get_properties_for. Could you review that?

I suppose, Z_IS_RECURSIVE_P/GC_IS_RECURSIVE were used on purpose, but I can't remember anything.

I also don't know, brainstorming ideas of why:

  1. Trying to get mixes of json_encode calling var_export to work instead of failing with RECURSION? - seems obscure and not likely. There are alternate approaches even if that really was the reason for the approach, e.g. new GC_FLAGS bits specifically for var_export/debug dumpers
  2. Trying to detect sharing the same hash table - I can't think of anything doing that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW this leads me to another idea - we may try to return the same ht if it's already protected from recursion.

    if (GC_IS_RECURSIVE(ht)) {
        return ht;
    }

It's protected from recursion when debugging - var_export/var_dump would show the wrong results if the side effects of __debugInfo, jsonSerialize, etc. of another method modified the underlying table during a json_encode/var_dump call, but it wouldn't crash or misbehave.

That's probably work in combination with adding an implementation of get_properties_for for other ZEND_PROP_PURPOSE_*, though with all the problems this has had so far, I'd rather introduce that in 8.2 rather than in a patch release. (especially with SplFixedArray being subclassable and subclasses being able to override JsonSerializable, __sleep, __debugInfo, etc.

(because json_encode, var_export, debug_zval_dump wouldn't use the hash table, they'd just report recursion).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting approach, but I afraid it's too expensive. I thought about setting some flag on SplFixedArray modification.

I pushed a new commit doing that instead

Closes phpGH-8079

Track whether the spl_fixedarray was modified since the last call to
get_properties
@TysonAndre TysonAndre force-pushed the PHP-8.0-SplFixedArray-loop branch from 3daa8e3 to 6e736d1 Compare February 17, 2022 13:05
Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fine to me.

@TysonAndre TysonAndre merged commit cd1c6f0 into php:PHP-8.0 Feb 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants